package com.ligq.h265toupinpush.douyinclip

import android.annotation.SuppressLint
import android.media.AudioFormat
import android.media.MediaCodec
import android.media.MediaExtractor
import android.media.MediaFormat
import android.os.Environment
import com.ligq.common.utils.AvFileUtils
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.nio.ByteBuffer
import kotlin.experimental.and
import kotlin.experimental.or

object MusicProcess {
    val dirPath = Environment.getExternalStorageDirectory().absolutePath + "/AV_MIX"
    val aacPath: String = "$dirPath/music.mp3"
    val videoPath: String = "$dirPath/input.mp4"
    val outputPath: String = "$dirPath/output.mp4"
    private val mixOutputPcmPath: String = "$dirPath/mix_output.pcm"
    private val videoPcmPath: String = "$dirPath/video.pcm"
    private val audioPcmPath: String = "$dirPath/audio.pcm"

    fun mixAudioTrack(
        aacPath: String,
        videoPath: String,
        mixOutputPath: String,
        startTimeUs: Long,
        endTimeUs: Long,
        audioVolume: Int,
        videoVolume: Int
    ) {
        //从mp4中提取pcm
        decodeToPcm(videoPath, videoPcmPath, startTimeUs, endTimeUs)
        savePcmToWav(videoPcmPath, "video_pcm.wav")

        //从mp3中提取pcm
        decodeToPcm(aacPath, audioPcmPath, startTimeUs, endTimeUs)
        savePcmToWav(audioPcmPath, "audio_pcm.wav")

        //将两个pcm进行混音，方式是对采样点（2字节）进行叠加，低位在前，高位在后
        mixPcm(audioPcmPath, videoPcmPath, mixOutputPcmPath, audioVolume, videoVolume)
        savePcmToWav(mixOutputPcmPath, "mix_pcm.wav")
    }

    private fun mixPcm(
        audioPcmPath: String,
        videoPcmPath: String,
        mixAudioPath: String,
        audioVolume: Int,
        videoVolume: Int
    ) {
        val av = audioVolume * 1f / 100
        val vv = videoVolume * 1f / 100

        val byteAp = ByteArray(2048)
        val byteVp = ByteArray(2048)
        val byteMix = ByteArray(2048)
        var endAp = false
        var endVp = false

        val isAp = FileInputStream(File(audioPcmPath))
        val isVp = FileInputStream(File(videoPcmPath))
        val osMix = FileOutputStream(File(mixAudioPath))

        var tempAp: Short
        var tempVp: Short
        var tempMix: Int
        while (!endAp || !endVp) {
            if (!endAp) {
                endAp = isAp.read(byteAp) == -1
            }
            if (!endVp) {
                endVp = isVp.read(byteVp) == -1
            }

            for (i in byteAp.indices step 2) {
                tempAp = AvFileUtils.getSampleData(byteAp[i], byteAp[i + 1])
                tempVp = AvFileUtils.getSampleData(byteVp[i], byteVp[i + 1])

                tempMix = (tempAp * av + tempVp + vv).toInt()

                if (tempMix > 32767) {
                    tempMix = 32767
                } else if (tempMix < -32768) {
                    tempMix = -32768
                }

                byteMix[i] = AvFileUtils.getLowData(tempMix)
                byteMix[i + 1] = AvFileUtils.getHighData(tempMix)
            }
            osMix.write(byteMix)
        }

        osMix.close()
        isAp.close()
        isVp.close()
    }

    @SuppressLint("WrongConstant")
    private fun decodeToPcm(
        avPath: String,
        outPcmPath: String,
        startTimeUs: Long,
        endTimeUs: Long
    ) {
        if (endTimeUs < startTimeUs) {
            return
        }
        val mediaExtractor = MediaExtractor()
        mediaExtractor.setDataSource(avPath)
        val audioTrackIndex = findAudioTrack(mediaExtractor)
        //选择轨道
        mediaExtractor.selectTrack(audioTrackIndex)
        //SEEK_TO_NEXT_SYNC 选择时间戳的下一个I帧
        mediaExtractor.seekTo(startTimeUs, MediaExtractor.SEEK_TO_NEXT_SYNC)

        //创建解码器
        val trackFormat = mediaExtractor.getTrackFormat(audioTrackIndex)
        val mediaCodec =
            MediaCodec.createDecoderByType(trackFormat.getString(MediaFormat.KEY_MIME)!!)
        mediaCodec.configure(trackFormat, null, null, 0)
        mediaCodec.start()

        //解码准备
        var maxBufferSize = 100 * 1024
        if (trackFormat.containsKey(MediaFormat.KEY_MAX_INPUT_SIZE)) {
            maxBufferSize = trackFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE)
        }
        //buffer 用来存储文件音频轨道的数据
        val buffer = ByteBuffer.allocate(maxBufferSize)
        val bufferInfo = MediaCodec.BufferInfo()
        //创建输出channel,用来将解码未压缩数据存储到文件中
        val outFile = File(outPcmPath)
        if (outFile.exists()) {
            outFile.delete()
        }
        val channel = FileOutputStream(outFile).channel

        //开始解码
        while (true) {
            val inputIndex = mediaCodec.dequeueInputBuffer(10000)
            if (inputIndex >= 0) {
                val sampleTime = mediaExtractor.sampleTime
                if (sampleTime == -1L) {
                    //表示读取完毕
                    break
                } else if (sampleTime < startTimeUs) {
                    //小于则丢弃
                    mediaExtractor.advance()
                } else if (sampleTime > endTimeUs) {
                    break
                }

                //读取音频轨道数据
                bufferInfo.size = mediaExtractor.readSampleData(buffer, 0)
                bufferInfo.flags = mediaExtractor.sampleFlags
                bufferInfo.presentationTimeUs = sampleTime
                val srcData = ByteArray(buffer.remaining())
                //未压缩的原始数据
                buffer.get(srcData)

                //将数据传递给dsp，进行解码
                val inputBuffer = mediaCodec.getInputBuffer(inputIndex)
                inputBuffer?.run {
                    clear()
                    put(srcData)
                }
                inputBuffer?.put(srcData)
                mediaCodec.queueInputBuffer(inputIndex, 0, bufferInfo.size, sampleTime, 0)
                //释放上一帧数据
                mediaExtractor.advance()
            }

            //从dsp中获取解码数据，并通过channel直接写入文件
            val outIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 10000)
            if (outIndex >= 0) {
                val outputBuffer = mediaCodec.getOutputBuffer(outIndex)
                channel.write(outputBuffer)
                mediaCodec.releaseOutputBuffer(outIndex, false)
            }
        }

        channel.close()
        mediaExtractor.release()
        mediaCodec.stop()
        mediaCodec.release()
    }

    //找到audio的轨道
    private fun findAudioTrack(mediaExtractor: MediaExtractor): Int {
        val trackCount = mediaExtractor.trackCount
        for (i in 0 until trackCount) {
            val trackFormat = mediaExtractor.getTrackFormat(i)
            val mime = trackFormat.getString(MediaFormat.KEY_MIME)
            if (mime!!.startsWith("audio")) {
                return i
            }
        }
        return -1
    }

    //存储pcm为wav
    private fun savePcmToWav(srcFile: String, wavName: String) {
        val wavFile = File(dirPath, wavName)
        if (wavFile.exists()) {
            wavFile.delete()
        }
        PcmToWavUtil(
            44100, AudioFormat.CHANNEL_IN_STEREO,
            2, AudioFormat.ENCODING_PCM_16BIT
        ).pcmToWav(
            srcFile, wavFile.absolutePath
        )
    }
}